from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseRedirect
from django.shortcuts import render, redirect, get_object_or_404
from django.template import RequestContext
from django.utils.encoding import force_text
from django.utils.functional import Promise
from django.db import transaction
from random import sample
import datetime, random, cPickle, re, json

from page.tree_utils import remove_page_from_tree, insert_page
from test.models import (Test, ProgressCheck, Exercise, 
    Contains, DiagnosticTest)
from question.models import Question, Answer, Difficulty
from page.models import Page
from engonline.models import UserAnswer
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.db.models import Q
from question.question_type import QUESTION_TYPES
from page.utils import get_active_diagnostic_test, set_active_diagnostic_test

def create_new_exercise(request, page_id):
    if request.method == 'GET':
        type = request.GET['type']
        page = request.GET['page']
        filter = request.GET['filter']
        difficulty = request.GET['difficulty']
        question_page = request.GET['question_page']
        
        
        questions = Question.objects.filter(trash=False)
        
        if filter:
            questions = questions.filter(
                    Q(question__icontains=filter) |
                    Q(created_by__username__icontains=filter) |
                    Q(page__title__icontains=filter)
            )
    
        if difficulty:
            questions = questions.filter(difficulty=difficulty)
    
        if type:
            questions = questions.filter(question_type=type)
    
        if question_page:
            questions = questions.filter(
                    page=get_object_or_404(Page, pk=question_page)
            )
        available_questions = questions
                
    return render(request, 'exercise.html', {'page_id':page_id, 'available_questions':available_questions})

def new_exercise(request):
    """Create a new exercise"""
    if request.method == 'POST':
        inc_questions = request.POST.getlist('question_ids','')
        page_id = request.POST.get('page','')

                
        question_order = 1
        test = get_object_or_404(Test, pk=page_id)
        
        question_order = Contains.objects.select_related('question_order').filter(test=page_id)
        x = 0
        for qo in question_order:
            if qo.question_order > x:
                x = qo.question_order
                x +=1
            
        for question in inc_questions:
            print question
            new_question = Question.objects.get(id=question)
            contains = Contains(test=test, question_order=x, 
                question=new_question)
            contains.save()
            x +=1    
                
        return HttpResponseRedirect(test.get_absolute_url())
    else:
        return redirect('test.views.create_new_exercise')

def filter_questions(request,page_id):

    available_questions = Question.objects.filter(trash=False)

    
    paginator = Paginator(available_questions, 10)
    page = request.GET.get('page')
    try:
        questions = paginator.page(page)
    except PageNotAnInteger:
        questions = paginator.page(1)
    except EmptyPage:
        questions = paginator.page(paginator.num_pages)

    difficulties = Difficulty.objects.all()
    question_types = QUESTION_TYPES
    question_pages = Page.objects.all()

    return render(request, 'filter_questions.html', 
            { 'difficulties': difficulties,
              'page_id':page_id, 
              'question_types': question_types, 
              'question_pages': question_pages, 
              'available_questions': questions }
    )

def create_new_progress(request, page_id):
    if request.method == 'GET':
        type = request.GET['type']
        page = request.GET['page']
        filter = request.GET['filter']
        difficulty = request.GET['difficulty']
        question_page = request.GET['question_page']
        
        
        questions = Question.objects.filter(trash=False)
        
        if filter:
            questions = questions.filter(
                    Q(question__icontains=filter) |
                    Q(created_by__username__icontains=filter) |
                    Q(page__title__icontains=filter)
            )
    
        if difficulty:
            questions = questions.filter(difficulty=difficulty)
    
        if type:
            questions = questions.filter(question_type=type)
    
        if question_page:
            questions = questions.filter(
                    page=get_object_or_404(Page, pk=question_page)
            )
        available_questions = questions
                
    return render(request, 'progress.html', {'page_id':page_id, 'available_questions':available_questions})

def new_progress_check(request):
    """Create a new progress check"""
    if request.method == 'POST':    
        inc_questions = request.POST.getlist('question_ids','')
        page_id = request.POST.get('page','')

                
        try:
            question_order = 1
            progress = get_object_or_404(ProgressCheck, pk=page_id)
            progress.save()
            
            question_order = Contains.objects.select_related('question_order').filter(test=page_id)
            x = 0
            for qo in question_order:
                if qo.question_order > x:
                    x = qo.question_order
                    x +=1
                
            for question in inc_questions:
                print question
                new_question = Question.objects.get(id=question)
                contains = Contains(test=progress, question_order=x, 
                    question=new_question)
                contains.save()
                x +=1    
                
        #If the parent page is not found,
        #create no exercise
        except ProgressCheck.DoesNotExist:
            progress = ProgressCheck(creator = request.user, changer = request.user)
            progress.save()
        
        return redirect('test.views.list_progress_checks')
    else:
        return redirect('test.views.create_new_progress')


def filter_for_progress(request,page_id):

    available_questions = Question.objects.filter(trash=False)

    
    paginator = Paginator(available_questions, 10)
    page = request.GET.get('page')
    try:
        questions = paginator.page(page)
    except PageNotAnInteger:
        questions = paginator.page(1)
    except EmptyPage:
        questions = paginator.page(paginator.num_pages)

    difficulties = Difficulty.objects.all()
    question_types = QUESTION_TYPES
    question_pages = Page.objects.all()

    return render(request, 'filter_for_progress.html', 
            { 'difficulties': difficulties,
              'page_id':page_id, 
              'question_types': question_types, 
              'question_pages': question_pages, 
              'available_questions': questions }
    )
def create_diagnostic_test(request):
    diagnostic_test = DiagnosticTest(title="New diagnostic test")
    diagnostic_test.save()
    return HttpResponseRedirect(diagnostic_test.get_absolute_url())

def create_new_diagnostic(request):
    if request.method == 'GET':
        type = request.GET['type']
        page = request.GET['page']
        filter = request.GET['filter']
        difficulty = request.GET['difficulty']
        question_page = request.GET['question_page']
        
        
        questions = Question.objects.filter(trash=False)
        
        if filter:
            questions = questions.filter(
                    Q(question__icontains=filter) |
                    Q(created_by__username__icontains=filter) |
                    Q(page__title__icontains=filter)
            )
    
        if difficulty:
            questions = questions.filter(difficulty=difficulty)
    
        if type:
            questions = questions.filter(question_type=type)
    
        if question_page:
            questions = questions.filter(
                    page=get_object_or_404(Page, pk=question_page)
            )
        available_questions = questions
                
    #return render(request, 'diagnostic.html', {'page_id':page_id, 'available_questions':available_questions})
    return render(request, 'diagnostic.html', {available_questions:'available_questions'})

    
def new_diagnostic(request):
    """Create a new diagnostic test"""
    if request.method == 'POST':    
        inc_questions = request.POST.getlist('question_ids','')
        page_id = request.POST.get('page','')

                
        try:
            question_order = 1
            diagnostic = get_object_or_404(DiagnosticTest, pk=page_id)
            diagnostic.save()
            
            question_order = Contains.objects.select_related('question_order').filter(test=page_id)
            x = 0
            for qo in question_order:
                if qo.question_order > x:
                    x = qo.question_order
                    x +=1
                
            for question in inc_questions:
                print question
                new_question = Question.objects.get(id=question)
                contains = Contains(test=diagnostic, question_order=x, 
                    question=new_question)
                contains.save()
                x +=1    
                
        #If the parent page is not found,
        #create no exercise
        except DiagnosticTest.DoesNotExist:
            diagnostic = DiagnosticTest(creator = request.user, changer = request.user)
            diagnostic.save()
        
        return redirect('test.views.list_diagnostic_tests')
    else:
        return redirect('test.views.create_new_diagnostic')
    
        
def filter_for_diagnostic(request,page_id):

    available_questions = Question.objects.filter(trash=False)

    
    paginator = Paginator(available_questions, 10)
    page = request.GET.get('page')
    try:
        questions = paginator.page(page)
    except PageNotAnInteger:
        questions = paginator.page(1)
    except EmptyPage:
        questions = paginator.page(paginator.num_pages)

    difficulties = Difficulty.objects.all()
    question_types = QUESTION_TYPES
    question_pages = Page.objects.all()

    return render(request, 'filter_for_diagnostic.html', 
            { 'difficulties': difficulties,
              'page_id':page_id, 
              'question_types': question_types, 
              'question_pages': question_pages, 
              'available_questions': questions }
    )
    
def add_extra(request, test_id):
    if request.method == 'POST':
        t = Contains.objects.get(id=test_id)
        test = t.test
        extra_questions = request.POST.getlist('extra_questions','')
        
        question_order = Contains.objects.select_related('question_order').filter(test=test)
        x = 0
        for qo in question_order:
            if qo.question_order > x:
                x = qo.question_order
        x +=1
        for question in extra_questions:
            new_question = Question.objects.get(id=question)
            contains = Contains(test=test, question_order=x, question=new_question)
            contains.save()
            x+=1
            
        available_questions = Question.objects.all()
        
        return render(request, 'add_extra.html', { 
            'available_questions':available_questions})

def listQuestions(request):
    """List all existing questions"""
    questions = Question.objects.all()
    return render(request, 'exercise.html', {'questions':questions})
    
def list_exercises(request):
    """List all existing exercises"""
    exercises = Exercise.objects.all()
    return render(request, 'exercises.html', {'exercises':exercises})

def list_progress_checks(request):
    """List all existing exercises"""
    progress_checks = ProgressCheck.objects.all()
    return render(request, 'progress_checks.html', {'progress_checks':progress_checks})

def list_diagnostic_tests(request):
    """List all existing exercises"""
    if request.method == 'POST':    
        diagnostic_id = request.POST.get('diagnostic_test')
        #Tries to find the requested diagnostic test.
        try:
            diagnostic_test = DiagnosticTest.objects.get(id=diagnostic_id)
            set_active_diagnostic_test(diagnostic_test)
        #Ignores invalid ids
        except DiagnosticTest.DoesNotExist:
            print "except"
            pass
    diagnostic_tests = DiagnosticTest.objects.filter(trash=False)
    ad = get_active_diagnostic_test()
    return render(request, 'diagnostic_tests.html', {'diagnostic_tests':diagnostic_tests, 'active':ad})

def trash_diagnostic_test(request):
    """Delete an existing type"""
    if request.method == 'POST':    
        diagnostic_id = request.POST.get('diagnostic_test')
        #Tries to find the requested diagnostic test.
        try:
            diagnostic_test = DiagnosticTest.objects.get(id=diagnostic_id)
            if(diagnostic_test == get_active_diagnostic_test()):
                set_active_diagnostic_test(None)
            diagnostic_test.trash = True
            diagnostic_test.save()
        #Ignores invalid ids
        except DiagnosticTest.DoesNotExist:
            print "except"
            pass 
     
        
    return redirect('test.views.list_diagnostic_tests')
    
    
def delete_exercise(request, exercise_id):
    """Delete an existing type"""
    exercise = get_object_or_404(Exercise, pk=exercise_id)
    remove_page_from_tree(exercise)
            
    exercise.delete()
    return redirect('test.views.list_exercises')

def delete_progress_check(request, progress_id):
    """Delete an existing type"""
    progress_check = get_object_or_404(ProgressCheck, pk=progress_id)
    remove_page_from_tree(progress_check)
    
    progress_check.delete()
    
    return redirect('test.views.list_progress_checks')

def delete_diagnostic_test(request):
    """Delete an existing type"""
    if request.method == 'POST':    
        diagnostic_id = request.POST.get('diagnostic_test')
        #Tries to find the requested diagnostic test.
        try:
            diagnostic_test = DiagnosticTest.objects.get(id=diagnostic_id)
            if(diagnostic_test == get_active_diagnostic_test()):
                set_active_diagnostic_test(None)
            diagnostic_test.delete()
        #Ignores invalid ids
        except DiagnosticTest.DoesNotExist:
            print "except"
            pass 
     
        
    return redirect('test.views.list_diagnostic_tests')    

def edit_exercise(request, exercise_id):
    """Show details about the specified question"""
    exercise = Contains.objects.select_related(
        'test').filter(test = exercise_id)
    
    if len(exercise) > 1:
        exercise = exercise[0]
    
    questions = Contains.objects.select_related(
        'question','question_order').filter(test = exercise_id)
    return render(request, 'view.html', {'exercise': exercise, 
        'questions':questions})

def edit_progress_check(request, progress_id):
    """Show details about the specified question"""
    progress_check = Contains.objects.select_related(
        'test').filter(test = progress_id)
    
    if len(progress_check) > 1:
        progress_check = progress_check[0]
    
    questions = Contains.objects.select_related(
        'question','question_order').filter(test = progress_id)
    return render(request, 'view_progress_check.html', {'progress_check': progress_check, 
        'questions':questions})
    
def edit_diagnostic_test(request, diagnostic_test_id):
    """Show details about the specified question"""
    diagnostic_test = Contains.objects.select_related(
        'test').filter(test = diagnostic_test_id)
    
    if len(diagnostic_test) > 1:
        diagnostic_test = diagnostic_test[0]
    
    questions = Contains.objects.select_related(
        'question','question_order').filter(test = diagnostic_test_id)
    return render(request, 'view.html', {'diagnostic_test': diagnostic_test, 
        'questions':questions})

def transform_to_test(request, test_type, page_id):
    """
    Transforms a page to an exercise.
    """
    if request.is_ajax() and request.method == 'POST':
        try:
            with transaction.commit_on_success():
                #Get page to transform
                page = Page.objects.get(id=page_id)
                test = None

                if test_type == 'progress_check':
                    test = ProgressCheck(page_ptr=page, questions_to_show=10)
                elif test_type == 'exercise':
                    test = Exercise(page_ptr=page)

                test.__dict__.update(page.__dict__)
                test.save()

        except Exception, e:
            return HttpResponse(status=400)

    return HttpResponse(status=200)

def remove_question(request, test_id):
    """"
    Removes a question from a test.
    """
    if request.is_ajax and request.method == 'POST':
        try:
            with transaction.commit_on_success():
                question_id_string = request.POST.get('question_id')
                
                #Match: letters_(question_id|null)
                m = re.match(r'\w+_(\d+|null)', question_id_string)
                question_id = m.group(1)

                relation = Contains.objects.get(test=test_id, question=question_id)
                relation.delete()

                return HttpResponse(json.dumps(question_id), 
                    content_type="application/json")

        except Exception, e:
            return HttpResponse(status=400)
    else:
        return HttpResponse(status=400)

def rearrange_questions_in_test(request, test_id):
    """
    Rearranges the questions of a test.
    """
    if request.is_ajax() and request.method == 'POST':
        try:
            with transaction.commit_on_success():
                question_structure = request.POST.get('question-structure')

                #Split questions on delimiter
                ordered_questions =  question_structure.split('&')

                order_index = 0
                for question in ordered_questions:
                    #Match: letters=(question_id|null)
                    m = re.match(r'\w+=(\d+|null)', question)
                    question_id = m.group(1)

                    #Fetch contains object
                    contains = Contains.objects.select_related().get(test=test_id, question=question_id)
                    contains.question_order = order_index
                    contains.save()
                    order_index += 1

        except Exception, e:
            return HttpResponse(status=400)

    return HttpResponse(status=200)